
import org.springframework.web.servlet.ModelAndView;

import org.json.JSONObject;

import org.groovyflow.json.*
import org.groovyflow.util.Money
import org.groovyflow.db.*

import org.groovyflow.*


import com.bus.PriceChangeValidations
import com.bus.PriceChange
import com.bus.ProductDao
import com.bus.Product


/*
Set an environment variable MODE either to 'dev' or 'prod', and then build.  
If you choose prod, this class is compiled and you can't modify
it when you deploy the webapp.  If you chosse 'dev', you can
modify this class, or any other class under the groovy source root,
and you'll see the effects of your change(s) without needing to 
restart your servlet engine.  (See this project's build.xml.)
Be sure not to deploy to production with dev MODE, because that mode
is not thread safe.

You access this class from the browser by typing 
http://yourServer:yourPort/webAppName/priceChange_listProducts.do.
That invokes the listProducts method of PriceChangeController.
Other url to class/method translation can also be supported,
with more on the way, including a way to switch from one to the other.
(See org.groovyflow.spring.web.StandardSuffixDynamicFinder)

Look for notes in the code below to learn about some of GroovyFlow's features.
Features include interceptors, partials, validations, a large chunk of ActiveRecord
functionality, and support for heavy use of JSON.
*/

class PriceChangeController {
    
    //Note: Define a before interceptor that is called on every method except 'savePriceChange'
	def before = ["securityCheck"]
	def securityCheckBefore = [except : ["savePriceChange"]]
	
    //Note: Define an after interceptor that will choose the layout.
    def after = ["layout"]
    
	
	def securityCheck(req, res){
		println "No security infrastructure set up yet, so this guy looks good to me."
	}
	
	def layout(req, res, modelAndView){
		//Note. This project uses SiteMesh, and has a DecoratorMapper that 
		//chooses the layout based on the value of the layout request attribute.
		//If your'e interested, see DecoratorMapper, sitemesh.xml, and decorators.xml,
		//but note that GroovyFlow doesn't really add anything to your use of 
		//SiteMesh, which is already fairly easy to use.
		req.setAttribute("layout","fancy")
	}

	def listProducts(req, res){
	   //Note: Here we get ActiveRecords.  In a real app we'd probably just
	   //get a list of Maps here, since there's on reason to go through anymore
	   //object creation than necessary for read only records.By the way, there 
	   //are no silly static methods on ActiveRecord.   See ActiveRecord and 
	   //SqlDaoImpl.
	   def products = new Product().findAll() 
	  
	   
	   //Note: Here we use a partial, but not on an Ajax call.  More 
	   //info on partials elsewhere in this class.
	   def moreBindings = ["products" : products]
	     
	   def allJsonMap = [productsTable:
	         GroovyFlowContext.get("partials").make("priceChange/_productsTable", moreBindings)
	           ]
	   	
	   //Note: GroovyFlow enhancement of HttpServletRequest gives it a method 
       //for writing JSON to a requestAttribute named 'jsonAttr'.  
       //(See org.groovyflow.aop.StartupAop)	  
	   req.putJson(allJsonMap)
	   
	   return new ModelAndView("priceChange/listProducts")
    }
    
    def editPrice(req, res){
    	//Note: ActiveRecords can add read-only join fields, as Product does here
    	//so that the view has the necessary information.  
    	def prod = new Product().findWithCategoryInfo(req.getParameter('id'))

    	//Note: Temporary session storage, as with Rails' flash,
    	//is on the way.
	    req.session.setAttribute("product", prod)
	    
	    formInReq(new PriceChange(), req)
	    
	    //Note: The ability to dispense with specifying
	    //the jsp/gsp name when the name is obvious may
	    //be on the way.  (I'm not sure I'll do it, because I think 
	    //it's fairly silly.  But I'll probably end up changing my mind.)
	    return new ModelAndView("priceChange/changePrice")
    }
    
    def savePriceChange(req, res){
    	println "SAVE"
    	def product = req.session.getAttribute("product")
    	def form = req.getJsonForm()
    	def errors = doValidations(form, product)
    	if(!errors.isEmpty()){
    		def allJsonMap = formInReq(form, req)
	    	allJsonMap.errors = errors
	    	return new ModelAndView("priceChange/changePrice")
    	}
    	println "FORM IS " + form
    	def priceChange = new PriceChange().sValues(form)
    	println "PRICE CHANGE IS " + priceChange + " with increase = " + priceChange.increase
    	//In the above call, PriceChange needs a constructor that takes a map.
    	//I probably prefer: def priceChange = new PriceChange().sValues(form)
    	product.changePrice(priceChange)
    	product.save()
        res.sendRedirect("priceChange_listProducts.do")
    }
    
    def ajax(req, res){
    	println "AR VALIDATE"
    	def product = req.session.getAttribute("product")
    	println "product from session is " + product
    	//Note: GroovyFlow enhancement of HttpServletRequest.
    	//Expects req.requestAttribute("jsonAttr") to hold the form's values in JSON
    	//format, as, for example, with dojo's formToJson method.
    	//Converts the Json information  into an object. 
    	//(See org.groovyflow.aop.StartupAop)
    	def form = req.getJsonForm()
    	
    	def errors = doValidations(form, product)
    	
    	def allJsonMap = [:]
    	allJsonMap.errors = errors
    	
    	if (errors.increase == null){
        	req.setAttribute("newPrice", product.addToPriceWhatIf(form.increase))
        	//Note:  Here's how to do Rails-like partials
        	//Of course you can change the partials file inside your 
        	//webapp and see changes take effect immediately.
        	//See PartialsBuilder and PartialsBuilderWrapper
    		allJsonMap.priceChange = 
    			GroovyFlowContext.get("partials").make("priceChange/_priceAfterChange");  	
    	}
    	
    	//Note: GroovyFlow enhancement of HttpServletResponse gives it a  method 
    	//for writing JSON out to  the response's PrintWriter.
    	//(See org.groovyflow.aop.StartupAop)
    	res.writeJson(allJsonMap)
    }
    
    def formInReq(form, req){
    	JSONObject formJson = new JSONObject(form)
    	def allJsonMap = ["formJson":formJson]
    	req.putJson(allJsonMap)
    	return allJsonMap
    }
    
    def doValidations(form, product){
		//Note: Take a look at the validations format in PriceChangeValidations.
    	//There we opt for something that's more like the Rails Presenter pattern
    	//(see http://blog.jayfields.com/2007/03/rails-presenter-pattern.html)
    	//than the typical Rails practice of sticking validations on the model object. 
    	//The latter  becomes very limiting when you have some validations that
    	//must be applied conditionally, or when your validations need to change depending
    	//upon which path you follow down a long workflow.
    	//Having said all that, I'll probably end up allowing the option of putting 
    	//validations into an ActiveRecord.  If I do so, these validations, unlike
    	//Rails ActiveRecord and GORM, will not be static.
    	def v = new PriceChangeValidations()
    	return v.doValidations(form, product)
    }
    
    
}